﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using iSteam.Sdk.Data;

namespace iSteam.Sdk
{
    public class SteamToken : BaseRequest
    {
        private Timer _timer;
        private TimeSpan? _interval = null;
        private AsyncOperation _async = null;
        private Core _core;

        public LoginStatus Authenticate(String username, String password, String emailauthcode = "")
        {
            var response = SteamRequest(Constants.SteamAuthUrl,
                                        String.Format(Constants.SteamAuthPost, Uri.EscapeDataString(username),
                                                      Uri.EscapeDataString(password), emailauthcode));

            if (response != null)
            {
                _accessToken = DataSerialise.Decrypt<AccessToken>(response);

                if (!string.IsNullOrEmpty(_accessToken.Token))
                {
                    if (Login())
                    {
                        _core = new Core(_accessToken, _loginToken);
                        _timer = new Timer(RetrieveMessagesAsync);
                        _timer.Change(new TimeSpan(0, 0, 0, 10), new TimeSpan(0, 0, 0, 10));
                        
                        return LoginStatus.LoginSuccessful;
                    }
                    else
                    {
                        return LoginStatus.LoginFailed;
                    }
                }

                return _accessToken.ErrorCode.Equals("steamguard_code_required") ? LoginStatus.SteamGuard : LoginStatus.LoginFailed;
            }

            return LoginStatus.LoginFailed;
        }
        public LoginStatus Authenticate(AccessToken accessToken)
        {
            this._accessToken = accessToken;
            return Login() ? LoginStatus.LoginSuccessful : LoginStatus.LoginFailed;
        }

        private bool Login()
        {
            String response = SteamRequest(Constants.SteamLoginUrl,
                                           string.Format(Constants.SteamLoginPost, _accessToken.Token));

            if (response != null)
            {
                _loginToken = DataSerialise.Decrypt<LoginToken>(response);

                if (!string.IsNullOrEmpty(_loginToken.Uid))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Cast RunWorkerCompletedEventArgs to NotificationCompletedEventArgs to get hold of the messages.
        /// </summary>
        public event RunWorkerCompletedEventHandler MessageReceived
        {
            add
            {
                this._messageReceivedEventHandler += value;
            }
            remove
            {
                this._messageReceivedEventHandler -= value;
            }
        }
        private RunWorkerCompletedEventHandler _messageReceivedEventHandler;

        public bool IsBusy
        {
            get { return _async != null; }
        }
        public List<Update> RetrieveMessages()
        {
            return _core.Poll().UpdateMessages;
        }
        public void RetrieveMessagesAsync(object state = null)
        {
            if (IsBusy)
                return;

            _async = AsyncOperationManager.CreateOperation(this);

            var handler =
                new SteamToken.TaskEventHandler(ProcessMessages);
            handler.BeginInvoke(_async, CompleteTask, null, null);
        }

        delegate void TaskEventHandler(AsyncOperation async, SendOrPostCallback callback);
        private void ProcessMessages(AsyncOperation async, SendOrPostCallback callback)
        {
            var poll = new PollMessage();
            Exception error = null;
            try
            {
                poll = _core.Poll();
            }
            catch (Exception ex)
            {
                error = ex;
            }
            if (poll != null)
            {
                callback(new object[]
                    {
                        new NotificationCompletedEventArgs(
                             null, error, false, poll),
                        async
                    });
            }
        }

        private void CompleteTask(object state)
        {
            object[] args = (object[])state;
            var e =
                args[0] as NotificationCompletedEventArgs;
            var async = args[1] as AsyncOperation;

            SendOrPostCallback callback = darg => OnMessageReceivedCompleted(darg as NotificationCompletedEventArgs);
            async.PostOperationCompleted(callback, e);
            _async = null;
        }

        protected virtual void OnMessageReceivedCompleted(NotificationCompletedEventArgs e)
        {
            if (_messageReceivedEventHandler != null)
                _messageReceivedEventHandler(this, e);
        }

        /// <summary>
        /// Current interval in seconds.
        /// </summary>
        public double Interval
        {
            get { return !_interval.HasValue ? double.NaN : _interval.Value.TotalSeconds; }
        }

        /// <summary>
        /// Allows for access to the Core functions. Returns the ICore interface.
        /// </summary>
        /// <returns></returns>
        public ICore CreateCore()
        {
            var core = new Core(AccessToken, LoginToken);
            return core;
        }
    }

    public class NotificationCompletedEventArgs : RunWorkerCompletedEventArgs
    {
        private readonly PollMessage _poll;
        public NotificationCompletedEventArgs(object result, Exception error, bool cancelled, PollMessage poll) : base (result, error, cancelled)
        {
            _poll = poll;
        }

        public PollMessage Poll
        {
            get { return _poll; }
        }
    }
}
